using java.lang;
using System.Collections.Generic;
using System.Windows.Media;

namespace Vec
{
	public class Shape
	{
		public List<Point> pts = new List<Point>();

		public int[] par = new int[]{
			-0x0fff0000,
		   -0x0f000000,
			0,
			100,
			0,
			0,
			0,
			-0x0f000000,
			0,
			0,
			0,
			0
	};

		//0:color,1:strokeColor,2:miter,3:strokeWidth
		//4:shadowD.x,5:.y,6:shadowR,7:shadowColor
		//8:dashPhase,9:segmentLength,10:deviation
		//11:radius
		public List<Point> linear = new List<Point>();

		public List<Point> radial = new List<Point>();
		public List<Point> sweep = new List<Point>();
		private WPFPath pa = new WPFPath(), pa2 = new WPFPath();
		private RectF rf = new RectF();
		private Matrix matrix = new Matrix();
		public string dashPattern = "";
		public long flag;
		public string txt = "";

		//private Xfermode xfermode;
		//private Shader shader;
		//private PathEffect pathEffect;
		public Shape(long flag)
		{
			this.flag = flag;
			int len = 0;
			if (hasFlag(TYPE.RECT) || hasFlag(TYPE.CIRCLE) || hasFlag(TYPE.OVAL) || hasFlag(TYPE.LINE) || hasFlag(TYPE.TEXT))
				len = 2;
			else if (hasFlag(TYPE.ROUNDRECT) || hasFlag(TYPE.ARC)) len = 4;
			else if (hasFlag(TYPE.PATH) || hasFlag(TYPE.POINT)) len = 1;
			for (int i = 0; i < len; i++)
				pts.Add(new Point(0, 0));
			if (hasFlag(TYPE.TEXT)) pts[1].x = 500;
		}

		public Shape(Shape src)
		{
			//copy flag
			flag = src.flag;
			//copy pts
			if (hasFlag(TYPE.PATH))
			{
				pts.Add(new Point(src.pts[0]));
				for (int i = 1; i < src.pts.Count; i++)
					pts.Add(new PathPoint((PathPoint)src.pts[i]));
			}
			else foreach (Point p in src.pts)
					pts.Add(new Point(p));
			//copy params
			java.lang.System.arraycopy(src.par, 0, par, 0, src.par.Length);
			//copy linear
			foreach (Point p in src.linear)
				linear.Add(new Point(p));
			//copy radial
			foreach (Point p in src.radial)
				radial.Add(new Point(p));
			//copy sweep
			foreach (Point p in src.sweep)
				sweep.Add(new Point(p));
			//copy dashPattern
			dashPattern = src.dashPattern;
			//copy txt
			txt = src.txt;
		}

		public static void Catmull_Rom(List<PointF> point, int cha, WPFPath path)
		{
			if (cha == 0) foreach (PointF p in point) path.lineTo(p.x, p.y);
			else if (point.Count > 1 && cha < 10000)
			{
				point.Insert(0, point[0]);
				point.Add(point[point.Count - 1]);
				for (int index = 1; index < point.Count - 2; index++)
				{
					PointF p0 = point[index - 1];
					PointF p1 = point[index];
					PointF p2 = point[index + 1];
					PointF p3 = point[index + 2];
					for (int i = 1; i <= cha; i++)
					{
						float t = i * (1.00f / cha);
						float tt = t * t;
						float ttt = tt * t;
						float x = (float)(0.5 * (2 * p1.x + (p2.x - p0.x) * t + (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * tt + (3 * p1.x - p0.x - 3 * p2.x + p3.x) * ttt));
						float y = (float)(0.5 * (2 * p1.y + (p2.y - p0.y) * t + (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * tt + (3 * p1.y - p0.y - 3 * p2.y + p3.y) * ttt));
						path.lineTo(x, y);
					}
				}
				path.lineTo(point[point.Count - 1].x, point[point.Count - 1].y);
			}
		}

		private static object getTypefaceByChar(object def, char charAt)
		{
			foreach (VECfile.TypefaceMap m in VECfile.typefaceMap)
			{
				if (charAt >= m.charSection[0] && charAt <= m.charSection[1])
				{
					return m.typeface;
				}
			}
			return def;
		}

		private static int getAC(int sc, int al)
		{
			return (int)((((sc & 0xff000000L) >> 24) * al / 256L) << 24 | sc & 0xffffffL);
		}

		public int getColor()
		{
			return par[0];
		}

		public void setColor(int color)
		{
			par[0] = color;
		}

		public int getStrokeColor()
		{
			return par[1];
		}

		public void setStrokeColor(int color)
		{
			par[1] = color;
		}

		public int getStrokeMiter()
		{
			return par[2];
		}

		public void setStrokeMiter(int m)
		{
			par[2] = m;
		}

		public int getStrokeWidth()
		{
			return par[3];
		}

		public void setStrokeWidth(int w)
		{
			par[3] = w;
		}

		public int getShadowX()
		{
			return par[4];
		}

		public void setShadowX(int p)
		{
			par[4] = p;
		}

		public int getShadowY()
		{
			return par[5];
		}

		public void setShadowY(int p)
		{
			par[5] = p;
		}

		public int getShadowR()
		{
			return par[6];
		}

		public void setShadowR(int p)
		{
			par[6] = p;
		}

		public int getShadowColor()
		{
			return par[7];
		}

		public void setShadowColor(int p)
		{
			par[7] = p;
		}

		public int getDashPhase()
		{
			return par[8];
		}

		public void setDashPhase(int p)
		{
			par[8] = p;
		}

		public int getSegmentLen()
		{
			return par[9];
		}

		public void setSegmengLen(int p)
		{
			par[9] = p;
		}

		public int getDeviation()
		{
			return par[10];
		}

		public void setDeviation(int p)
		{
			par[10] = p;
		}

		public int getRadius()
		{
			return par[11];
		}

		public void setRadius(int p)
		{
			par[11] = p;
		}

		public bool hasFlag(long f)
		{
			return (flag & f) != 0;
		}

		public void setFlag(Shape src, long all)
		{
			long sr = src.flag;
			flag = (flag | all) - (sr | all) + sr;
		}

		public void setFlag(long f, long all)
		{
			flag = (flag | all) - all + f;
		}

		public void notifyPathChanged()
		{
			pa.reset();
		}

		public void notifyShaderChanged()
		{
			//shader = null;
		}

		public void notifyXferChanged()
		{
			//xfermode = null;
		}

		public void notifyPathEffectChanged()
		{
			//pathEffect = null;
		}

		public WPFPath getPath()
		{
			return pa;
		}

		public WPFPath getMappedPath()
		{
			return pa2;
		}

		public void draw(WPFCanvas c, float xx, float yy, float xsc, float ysc, int alpha, float dp, WPFPaint sp)
		{
			matrix.Scale(xsc * dp, ysc * dp);
			matrix.Translate(xx, yy);
			draw(c, matrix, alpha, sp);
		}

		//原比例绘制，不使用精度选项
		public void draw(WPFCanvas c, Matrix matrix, int alpha, WPFPaint sp)
		{
			/*float[] mp = new float[9];
            matrix.getValues(mp);
            float xsc = mp[Matrix.MSCALE_X], ysc = mp[Matrix.MSCALE_Y];
            float xx = mp[Matrix.MTRANS_X], yy = mp[Matrix.MTRANS_Y];*/
			float xsc = (float)matrix.M11, ysc = (float)matrix.M22;
			float xx = (float)matrix.OffsetX, yy = (float)matrix.OffsetY;
			//cap
			WPFPaint.Cap cp = WPFPaint.Cap.ROUND;
			if (hasFlag(STROKE.BUTT) && !hasFlag(TYPE.POINT)) cp = WPFPaint.Cap.BUTT;
			else if (hasFlag(STROKE.SQUARE)) cp = WPFPaint.Cap.SQUARE;
			sp.setStrokeCap(cp);
			//join
			WPFPaint.Join jn = WPFPaint.Join.ROUND;
			if (hasFlag(STROKE.BEVEL)) jn = WPFPaint.Join.BEVEL;
			else if (hasFlag(STROKE.MITER)) jn = WPFPaint.Join.MITER;
			sp.setStrokeJoin(jn);
			//stroke params
			sp.setStrokeMiter((float)par[2] / 100f * xsc);
			sp.setStrokeWidth((float)par[3] / 100f * xsc);
			//shadow
			sp.setShadowLayer((float)par[6] / 100f * xsc, par[4] * xsc, par[5] * ysc, par[7]);
			//path effect
			//unsupported
			//if (pathEffect == null) pathEffect = createPathEffect(xsc);
			//sp.setPathEffect(pathEffect);
			//shader
			//if (shader == null) shader = createShader(alpha);
			//if (shader != null) shader.setLocalMatrix(matrix);
			//sp.setShader(shader);
			//xfermode
			//if (xfermode == null) xfermode = getXfer();
			//sp.setXfermode(xfermode);
			//path
			if (pa.isEmpty()) addPath();
			if (hasFlag(SHAPEPAR.NEWLAYER))
				c.saveLayer(0, 0, c.getWidth(), c.getHeight(), sp, WPFCanvas.ALL_SAVE_FLAG);
			if (hasFlag(TYPE.TEXT))
			{
				//unsupported
				/*Point r = pts[0];
                Point t = pts[1];
                sp.setTextSize(t.x * xsc / 100f);
                sp.setTextScaleX(xsc / ysc);
                *//*Typeface mTypeface = VECfile.VTypeface.DEFAULT;
                if (hasFlag(TEXT.BOLD_TYPE)) mTypeface = VECfile.VTypeface.DEFAULT_BOLD;
                else if (hasFlag(TEXT.MONOSPACE)) mTypeface = VECfile.VTypeface.MONOSPACE;
                else if (hasFlag(TEXT.SANS_SERIF)) mTypeface = VECfile.VTypeface.SANS_SERIF;
                else if (hasFlag(TEXT.SERIF)) mTypeface = VECfile.VTypeface.SERIF;
                sp.setTypeface(mTypeface);*//*
                string[] lines = txt.Split('\n');
                float drawY = r.y * ysc + yy;
                if (isDrawOnce()) configPaint(sp, alpha);
                else foreach(string curLine in  lines)
                    {
                        twiceStyle(sp, alpha);
                        float drawX = r.x * xsc + xx, maxTextWidth = 0;
                        //测量总体宽度
                        for (int e = 0; e < curLine.Length; e++)
                        {
                            char charAt = curLine.CharAt(e);
                            sp.setTypeface(getTypefaceByChar(mTypeface, charAt));
                            maxTextWidth += sp.measureText(Character.toString(charAt));
                        }
                        if (hasFlag(TEXT.RIGHT)) drawX -= maxTextWidth;
                        else if (hasFlag(TEXT.CENTER)) drawX -= maxTextWidth / 2;
                        for (int e = 0; e < curLine.length(); e++)
                        {
                            char charAt = curLine.charAt(e);
                            String ssss = Character.toString(charAt);
                            sp.setTypeface(getTypefaceByChar(mTypeface, charAt));
                            c.drawText(ssss, drawX, drawY, sp);
                            drawX += sp.measureText(ssss);
                        }
                        restoreStyle(sp, alpha);
                        drawY += sp.getTextSize();
                    }
                drawY = r.y * ysc + yy;
                foreach(String curLine in  lines)
                {
                    float drawX = r.x * xsc + xx, maxTextWidth = 0;
                    //测量总体宽度
                    for (int e = 0; e < curLine.length(); e++)
                    {
                        char charAt = curLine.charAt(e);
                        sp.setTypeface(getTypefaceByChar(mTypeface, charAt));
                        maxTextWidth += sp.measureText(Character.toString(charAt));
                    }
                    if (hasFlag(TEXT.RIGHT)) drawX -= maxTextWidth;
                    else if (hasFlag(TEXT.CENTER)) drawX -= maxTextWidth / 2;
                    for (int e = 0; e < curLine.length(); e++)
                    {
                        char charAt = curLine.charAt(e);
                        String ssss = Character.toString(charAt);
                        sp.setTypeface(getTypefaceByChar(mTypeface, charAt));
                        c.drawText(ssss, drawX, drawY, sp);
                        drawX += sp.measureText(ssss);
                    }
                    drawY += sp.getTextSize();
                }*/
			}
			else
			{
				//draw
				pa.transform(matrix, pa2);
				if (isDrawOnce()) configPaint(sp, alpha);
				else
				{
					twiceStyle(sp, alpha);
					c.drawPath(pa2, sp);
					restoreStyle(sp, alpha);
				}
				c.drawPath(pa2, sp);
			}
			if (hasFlag(SHAPEPAR.RESTORELAYER)) c.restore();
		}

		/* private PorterDuffXfermode getXfer()
         {
             PorterDuff.Mode xm = null;
             if (hasFlag(XFERMODE.ADD)) xm = PorterDuff.Mode.ADD;
             else if (hasFlag(XFERMODE.CLEAR)) xm = PorterDuff.Mode.CLEAR;
             else if (hasFlag(XFERMODE.DARKEN)) xm = PorterDuff.Mode.DARKEN;
             else if (hasFlag(XFERMODE.DST)) xm = PorterDuff.Mode.DST;
             else if (hasFlag(XFERMODE.DST_ATOP)) xm = PorterDuff.Mode.DST_ATOP;
             else if (hasFlag(XFERMODE.DST_IN)) xm = PorterDuff.Mode.DST_IN;
             else if (hasFlag(XFERMODE.DST_OUT)) xm = PorterDuff.Mode.DST_OUT;
             else if (hasFlag(XFERMODE.DST_OVER)) xm = PorterDuff.Mode.DST_OVER;
             else if (hasFlag(XFERMODE.LIGHTEN)) xm = PorterDuff.Mode.LIGHTEN;
             else if (hasFlag(XFERMODE.MULTIPLY)) xm = PorterDuff.Mode.MULTIPLY;
             else if (hasFlag(XFERMODE.OVERLAY)) xm = PorterDuff.Mode.OVERLAY;
             else if (hasFlag(XFERMODE.SCREEN)) xm = PorterDuff.Mode.SCREEN;
             else if (hasFlag(XFERMODE.SRC)) xm = PorterDuff.Mode.SRC;
             else if (hasFlag(XFERMODE.SRC_ATOP)) xm = PorterDuff.Mode.SRC_ATOP;
             else if (hasFlag(XFERMODE.SRC_IN)) xm = PorterDuff.Mode.SRC_IN;
             else if (hasFlag(XFERMODE.SRC_OUT)) xm = PorterDuff.Mode.SRC_OUT;
             else if (hasFlag(XFERMODE.SRC_OVER)) xm = PorterDuff.Mode.SRC_OVER;
             else if (hasFlag(XFERMODE.XOR)) xm = PorterDuff.Mode.XOR;
             if (xm != null) return new PorterDuffXfermode(xm);
             return null;
         }*/

		private void addPath()
		{
			pa.reset();
			WPFPath.Direction dir = WPFPath.Direction.CW;
			if (hasFlag(TYPE.RECT))
			{
				Point p1 = pts[0];
				Point p2 = pts[1];
				rf.set(p1.x, p1.y, p2.x, p2.y);
				pa.addRect(rf, dir);
			}
			else if (hasFlag(TYPE.CIRCLE))
			{
				Point p1 = pts[0];
				Point p2 = pts[1];
				float r = (float)Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
				pa.addCircle(p1.x, p1.y, r, dir);
			}
			else if (hasFlag(TYPE.OVAL))
			{
				Point p1 = pts[0];
				Point p2 = pts[1];
				rf.set(p1.x, p1.y, p2.x, p2.y);
				pa.addOval(rf, dir);
			}
			else if (hasFlag(TYPE.ARC))
			{
				Point p1 = pts[0];
				Point p2 = pts[1];
				Point p3 = pts[2];
				Point p4 = pts[3];
				float cx = (p1.x + p2.x) / 2f;
				float cy = (p1.y + p2.y) / 2f;
				float r1 = (float)Math.sqrt(Math.pow((p3.x - cx), 2) + Math.pow((p3.y - cy), 2));
				float r2 = (float)Math.sqrt(Math.pow((p4.x - cx), 2) + Math.pow((p4.y - cy), 2));
				float start = (float)(Math.asin((p3.y - cy) / r1) * 180f / Math.PI);
				float end = (float)(Math.asin((p4.y - cy) / r2) * 180f / Math.PI);
				if (p3.x - cx < 0) start = 180f - start;
				if (p3.x - cx > 0 && p3.y - cy < 0) start += 360f;
				if (p4.x - cx < 0) end = 180f - end;
				if (p4.x - cx > 0 && p4.y - cy < 0) end += 360f;
				if (start > end) end += 360f;
				if (hasFlag(SHAPEPAR.CENTER))
					pa.moveTo((p2.x + p1.x) / 2f, (p2.y + p1.y) / 2f);
				rf.set(Math.min(p1.x, p2.x), Math.min(p1.y, p2.y), Math.max(p1.x, p2.x), Math.max(p1.y, p2.y));
				pa.arcTo(rf, start, end - start, false);
				if (hasFlag(SHAPEPAR.CENTER))
					pa.close();
			}
			else if (hasFlag(TYPE.ROUNDRECT))
			{
				Point p1 = pts[0];
				Point p2 = pts[1];
				Point p3 = pts[2];
				float r1 = Math.abs(p3.x - p1.x);
				float r2 = Math.abs(p3.y - p1.y);
				rf.set(p1.x, p1.y, p2.x, p2.y);
				pa.addRoundRect(rf, r1, r2, dir);
			}
			else if (hasFlag(TYPE.POINT))
			{
				Point r = pts[0];
				pa.moveTo(r.x, r.y);
				pa.lineTo(r.x, r.y);
			}
			else if (hasFlag(TYPE.LINE))
			{
				Point r1 = pts[0], r2 = pts[1];
				pa.moveTo(r1.x, r1.y);
				pa.lineTo(r2.x, r2.y);
			}
			else if (hasFlag(TYPE.PATH))
			{
				WPFPaint tp = new WPFPaint();
				tp.setTextSize(16);
				Point a = pts[0];
				if (pts.Count > 1)
				{
					List<PointF> poi = new List<PointF>();
					for (int i = 1; i < pts.Count; i++)
					{
						Point t = pts[i];
						PointF pf = new PointF(t.x, t.y);
						byte by = ((PathPoint)t).type;
						if (by == 1)
						{
							poi.Add(pf);
							Catmull_Rom(poi, a.y, pa);
							if (a.x == 1) pa.close();
							poi.Clear();
							poi.Add(pf);
						}
						else if (by == 2 || i == 1)
						{
							Catmull_Rom(poi, a.y, pa);
							if (a.x == 1) pa.close();
							poi.Clear();
							poi.Add(pf);
							pa.moveTo(pf.x, pf.y);
						}
						else poi.Add(pf);
					}
					Catmull_Rom(poi, a.y, pa);
					poi.Clear();
					if (a.x == 1) pa.close();
				}
			}
		}

		private bool isDrawOnce()
		{
			return hasFlag(STYLE.FILL | STYLE.STROKE);
		}

		private void configPaint(WPFPaint sp, int alpha)
		{
			sp.setColor(getAC(hasFlag(STYLE.FILL) ? par[0] : par[1], alpha));
			sp.setStyle(hasFlag(STYLE.STROKE | TYPE.LINE | TYPE.POINT) ? WPFPaint.Style.STROKE : WPFPaint.Style.FILL);
		}

		private void twiceStyle(WPFPaint sp, int alpha)
		{
			sp.setColor(getAC(hasFlag(STYLE.STROKE_FILL) ? par[0] : par[1], alpha));
			sp.setStyle(hasFlag(STYLE.STROKE_FILL) ? WPFPaint.Style.FILL : WPFPaint.Style.STROKE);
		}

		private void restoreStyle(WPFPaint sp, int alpha)
		{
			sp.setColor(getAC(hasFlag(STYLE.FILL_STROKE) ? par[0] : par[1], alpha));
			sp.setStyle(hasFlag(STYLE.FILL_STROKE) ? WPFPaint.Style.FILL : WPFPaint.Style.STROKE);
		}

		public void set(Shape src)
		{
			//foreach(Point p in src.pts)pts.Add(new Point(p.x,p.y));
			int i = 0;
			foreach (int p in src.par) par[i++] = p;
			dashPattern = src.dashPattern;
			setFlag(src, STYLE.ALL);
			setFlag(src, XFERMODE.ALL);
			setFlag(src, STROKE.ALL_CAP);
			setFlag(src, STROKE.ALL_JOIN);
			setFlag(src, SHAPEPAR.NEWLAYER + SHAPEPAR.RESTORELAYER);
		}

		/*        public bool equals(Object o)
                {
                    if (o instanceof Shape){
                        Shape s = (Shape)o;
                        bool e = s.flag == flag && txt.equals(s.txt) && pts.Count == s.pts.Count;
                        for (int i = 0; e && i < par.length; i++)
                            if (par[i] != s.par[i])
                            {
                                e = false;
                                break;
                            }
                        for (int i = 0; e && i < pts.Count; i++)
                            if (!pts[(i)].equals(s.pts[i])) e = false;
                        return e;
                    }else return false;
                }*/
		/*private PathEffect createPathEffect(float dp)
        {
            PathEffect l = null, r = null, s = null, eff;
            if (dashPattern != null && !dashPattern.isEmpty())
            {
                try
                {
                    String[] d = dashPattern.split(",");
                    float[] ps = new float[d.length];
                    int k = 0;
                    foreach(String x in  d) ps[k++] = Float.parseFloat(x) * dp;
                    l = new DashPathEffect(ps, (float)par[8] / 100f * dp);
                }
                catch (Throwable ignored)
                {
                }
            }
            if (par[9] != 0 && par[10] != 0)
                r = new DiscretePathEffect((float)par[9] / 100f * dp, (float)par[10] / 100f * dp);
            if (par[11] != 0) s = new CornerPathEffect((float)par[11] / 100f * dp);
            if (l == null && r == null && s == null) return null;
            if (l == null)
                if (r == null)
                    eff = s;
                else if (s == null) eff = r;
                else eff = new ComposePathEffect(r, s);
            else if (r == null)
                if (s == null) eff = l;
                else eff = new ComposePathEffect(l, s);
            else if (s == null) eff = new ComposePathEffect(l, r);
            else eff = new ComposePathEffect(
                        new ComposePathEffect(l, r)
                        , s);
            return eff;
        }
        private Shader createShader(int alpha)
        {
            Shader l = null, r = null, s = null, shader;
            if (!linear.isEmpty())
            {
                Point p1 = linear[0], p2 = linear[1];
                int[] c = new int[linear.Count - 2];
                float[] p = new float[c.length];
                for (int i = 0; i < c.length; i++)
                {
                    Point f = linear[i + 2];
                    c[i] = getAC(f.x, alpha);
                    p[i] = (float)f.y / 100f;
                }
                Shader.TileMode m = Shader.TileMode.CLAMP;
                if (hasFlag(TILEMODE.L_MIRROR)) m = Shader.TileMode.MIRROR;
                else if (hasFlag(TILEMODE.L_REPEAT)) m = Shader.TileMode.REPEAT;
                l = new LinearGradient(p1.x, p1.y, p2.x, p2.y, c, p, m);
            }
            if (!radial.isEmpty())
            {
                Point p1 = radial[0], p2 = radial[1];
                float rr = (float)Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
                int[] c = new int[radial.Count - 2];
                float[] p = new float[c.length];
                for (int i = 0; i < c.length; i++)
                {
                    Point f = radial[i + 2];
                    c[i] = getAC(f.x, alpha);
                    p[i] = (float)f.y / 100f;
                }
                Shader.TileMode m = Shader.TileMode.CLAMP;
                if (hasFlag(TILEMODE.R_MIRROR)) m = Shader.TileMode.MIRROR;
                else if (hasFlag(TILEMODE.R_REPEAT)) m = Shader.TileMode.REPEAT;
                r = new RadialGradient(p1.x, p1.y, rr, c, p, m);
            }
            if (!sweep.isEmpty())
            {
                Point p1 = sweep[0];
                int[] c = new int[sweep.Count - 1];
                float[] p = new float[c.length];
                for (int i = 0; i < c.length; i++)
                {
                    Point f = sweep[i + 1];
                    c[i] = getAC(f.x, alpha);
                    p[i] = (float)f.y / 100f;
                }
                s = new SweepGradient(p1.x, p1.y, c, p);
            }
            if (l == null && r == null && s == null) return null;
            if (l == null)
                if (r == null)
                    shader = s;
                else if (s == null) shader = r;
                else shader = new ComposeShader(r, s, PorterDuff.Mode.ADD);
            else if (r == null)
                if (s == null) shader = l;
                else shader = new ComposeShader(l, s, PorterDuff.Mode.ADD);
            else if (s == null) shader = new ComposeShader(l, r, PorterDuff.Mode.ADD);
            else shader = new ComposeShader(
                        new ComposeShader(l, r, PorterDuff.Mode.ADD)
                        , s, PorterDuff.Mode.ADD);
            return shader;
        }*/

		public static class TEXT
		{
			public static long
					LEFT = 0x1L,
					CENTER = 0x2L,
					RIGHT = 0x4L,
					DEFAULT_TYPE = 0x8L,
					BOLD_TYPE = 0x10L,
					MONOSPACE = 0x20L,
					SANS_SERIF = 0x40L,
					SERIF = 0x80L,
					ALL_ALIGN = LEFT + CENTER + RIGHT,
					ALL_TYPEFACE = DEFAULT_TYPE + BOLD_TYPE + MONOSPACE + SANS_SERIF + SERIF;
		}

		public static class STROKE
		{
			public static long
					BUTT = 0x100L,
					ROUND_CAP = 0x200L,
					SQUARE = 0x400L,
					ROUND_JOIN = 0x800L,
					MITER = 0x1000L,
					BEVEL = 0x2000L,
					ALL_CAP = BUTT + ROUND_CAP + SQUARE,
					ALL_JOIN = ROUND_JOIN + MITER + BEVEL;
		}

		public static class STYLE
		{
			public static long
					FILL = 0x4000L,
					STROKE = 0x8000L,
					FILL_STROKE = 0x10000L,
					STROKE_FILL = 0x20000L,
					ALL = FILL + STROKE + FILL_STROKE + STROKE_FILL;
		}

		public static class XFERMODE
		{
			public static long
					CLEAR = 0x40000L,
					DARKEN = 0x80000L,
					DST = 0x100000L,
					DST_ATOP = 0x200000L,
					DST_IN = 0x400000L,
					DST_OUT = 0x800000L,
					DST_OVER = 0x1000000L,
					LIGHTEN = 0x2000000L,
					MULTIPLY = 0x4000000L,
					OVERLAY = 0x8000000L,
					SCREEN = 0x10000000L,
					SRC = 0x20000000L,
					SRC_ATOP = 0x40000000L,
					SRC_IN = 0x80000000L,
					SRC_OUT = 0x100000000L,
					SRC_OVER = 0x200000000L,
					XOR = 0x400000000L,
					ADD = 0x800000000L,
					ALL = CLEAR +
							DARKEN +
							DST +
							DST_ATOP +
							DST_IN +
							DST_OUT +
							DST_OVER +
							LIGHTEN +
							MULTIPLY +
							OVERLAY +
							SCREEN +
							SRC +
							SRC_ATOP +
							SRC_IN +
							SRC_OUT +
							SRC_OVER +
							XOR +
							ADD;
		}

		public static class SHAPEPAR
		{
			public static long
					NEWLAYER = 0x1000000000L,
					RESTORELAYER = 0x2000000000L,
					CENTER = 0x4000000000L;
		}

		public static class TYPE
		{
			public static long
					RECT = 0x800000000000L,
					CIRCLE = 0x1000000000000L,
					OVAL = 0x2000000000000L,
					ARC = 0x4000000000000L,
					ROUNDRECT = 0x8000000000000L,
					PATH = 0x10000000000000L,
					POINT = 0x20000000000000L,
					LINE = 0x40000000000000L,
					TEXT = 0x80000000000000L,
					ALL =
							RECT +
									CIRCLE +
									OVAL +
									ARC +
									ROUNDRECT +
									PATH +
									POINT +
									LINE +
									TEXT;
		}

		public static class TILEMODE
		{
			public static long
					L_CLAMP = 0x100000000000000L,
					L_MIRROR = 0x200000000000000L,
					L_REPEAT = 0x400000000000000L,
					R_CLAMP = 0x800000000000000L,
					R_MIRROR = 0x1000000000000000L,
					R_REPEAT = 0x2000000000000000L,
					L_ALL =
							L_CLAMP +
									L_MIRROR +
									L_REPEAT,
					R_ALL =
							R_CLAMP +
									R_MIRROR +
									R_REPEAT;//62个
		}

		public class PathPoint : Point
		{
			public byte type;//normal:0,turn:1,start:2

			public PathPoint(int x, int y) : this(x, y, 0)
			{
			}

			public PathPoint(PathPoint p) : this(p.x, p.y, p.type)
			{
			}

			public PathPoint(int x, int y, int type) : base(x, y)
			{
				this.type = (byte)type;
			}

			public PathPoint() : this(0, 0, 0)
			{
			}
		}
	}
}